/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2009, 2010.

	This file is part of Maxsi Engine.

	Maxsi Engine is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi engine is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
	for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	MaxsiEngine.dll
	A general purpose C++ library for the Maxsi Technology Project.

	MaxsiServer.cpp
	Basic Winsock Functionallity for server handling.

******************************************************************************/

#include "MaxsiEngine.h"

#if 0

#include <io.h>
#include <direct.h>
#include <fcntl.h>
#include <SYS\Stat.h>
#include "winsock2.h"
#include "Mswsock.h"

BeginMaxsiNamespace

	//=============================================================================
//	class ME_Server
//=============================================================================

ME_Server::ME_Server()
{
	Clients				=		0;
	DebugMode			=		false;
	LogFile				=		INVALID_HANDLE_VALUE;
	Listener			=		INVALID_SOCKET;
}
ME_Server::~ME_Server()
{
	// Todo!
}

//=============================================================================
//	class ME_Server - Functions
//=============================================================================


char*	ME_Server::GetServerInformationHeader()
{
	return "Yeah, Whatever!";
	//return	BuildString(1,
	//	_MESTR("Maxsi_BuildVersion: ") Maxsi_BuildVersion _MESTR("\r\n")
	//	_MESTR("ME_Server_Version: ") ME_Server_Version _MESTR("\r\n")
	//	);
}
char*	ME_Server::GetServerInformationLine()
{
	// Get the date
	__int64		LocalTime;
	tm			CurrentTime;
	char		CurrentTime_str[26];
	errno_t		err;
	char*		Result;

	_time64( &LocalTime );

	err = _gmtime64_s( &CurrentTime, &LocalTime );
	if (err){return	0;}

	err = asctime_s(CurrentTime_str, 26, &CurrentTime);
	if (err){return	0;}

	Result	=	BuildString(2,CurrentTime_str, " Event: ");

	return	Result;
}

bool	ME_Server::ShutDown()
{
	return true;
}
bool	ME_Server::OpenLogFile(char* Path)
{
	
	LogFile	=	CreateFileA(Path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if ( LogFile == INVALID_HANDLE_VALUE) return false;

	char*	Header		=	GetServerInformationHeader();

	WriteFile(LogFile, Header, (DWORD)strlen(Header), NULL, NULL);
	
	delete[] Header;

	return true;
}
bool	ME_Server::CloseLogFile()
{
	CloseHandle(LogFile);
	LogFile		=	INVALID_HANDLE_VALUE;

	return true;
}
bool	ME_Server::ClearLogFile()
{
	// TODO: Get the path and clear it!
	return false;
}
bool	ME_Server::ClearLogFile(char* Path)
{
	if ( !Path ) { return 0; }
	// Deletes the log file

	return remove(Path)==0;
}
bool	ME_Server::SetDebugMode(bool IsDebugMode)
{
	DebugMode	=	IsDebugMode;
	return true;
}
bool	ME_Server::GetDebugMode()
{
	return	DebugMode;
}
bool	ME_Server::Log(char* Event)
{
	if ( LogFile == INVALID_HANDLE_VALUE ) { return false; }
		
	char*	Leading		=	GetServerInformationLine();

	WriteFile(LogFile, Leading, (DWORD)strlen(Leading), NULL, NULL);
	WriteFile(LogFile, Event, (DWORD)strlen(Event), NULL, NULL);
	WriteFile(LogFile, "\r\n", (DWORD)strlen("\r\n"), NULL, NULL);
	
	delete[] Leading;

	return true;
}
bool	ME_Server::LogDebug(char* Event)
{
	if ( !GetDebugMode() )
		return false;
	return Log(Event);
}

bool	ME_Server::LinkToWindow(HWND Window)
{
	WindowHWND		=	Window;
	return true;
}

//=============================================================================
//	class ME_IOCPServer - Constructors
//=============================================================================

ME_IOCPServer::ME_IOCPServer()
{
	TerminateEvent		=		0;
	MaxConnections		=		ME_IOCPServer_MaxConnections;
	MaintenanceTime		=		ME_IOCPServer_MaintenanceTime;
	ShouldMainLoop		=		true;
	ShouldListen		=		true;
}

ME_IOCPServer::~ME_IOCPServer()
{
	if ( Clients )
	{
		// Done
		for (size_t i = 0; i < MaxConnections; i++)
		{
			if ( Clients[i] )
			{
				Clients[i]->Close();
				Clients[i]->TerminateBuffers();
				delete Clients[i];
			}
		}
		delete[] Clients;
	}

	if ( Listener != INVALID_SOCKET ) { closesocket(Listener); }

	if ( TerminateEvent ) { CloseHandle(TerminateEvent); }
	
}

//=============================================================================
//	class ME_IOCPServer - Functions
//=============================================================================

bool	ME_IOCPServer::HandleRequest(ME_ServerClient* Client, char* Parameter)
{
	return false;
}

bool	ME_IOCPServer::RunServerEx(short Port, size_t NumConnections, size_t CheckTime)
{
	MaxConnections		=		NumConnections;
	MaintenanceTime		=		CheckTime;

	return RunServer(Port);
}

ME_ServerClient*	ME_IOCPServer::CreateIOCPClient( void )
{
	return new ME_ServerClient;
}

bool	ME_IOCPServer::RunServer(short Port)
{
	// Initialize an event that when run terminates this server
	
	size_t	i			=	0;

	if ( ShouldListen )
	{
		this->Port			=	Port;
		TerminateEvent		=	CreateEvent(0, true, false, 0);

		WSADATA wd = {0};
		if (0 != WSAStartup(MAKEWORD(2, 0), &wd) )
		{
			return false;
		}

		SOCKADDR_IN addr;

		if (INVALID_SOCKET == (Listener = socket(AF_INET, SOCK_STREAM, 0)) )
		{
			return false;
		}

		addr.sin_family			=	AF_INET;
		addr.sin_addr.s_addr	=	INADDR_ANY;
		addr.sin_port			=	htons((short)Port);

		if (0 != bind(Listener, (SOCKADDR *)&addr, sizeof(addr)) )
		{
			return false;
		}
		if (0 != listen(Listener, 200 /* SOMAXCONN or MaxConnections or the hardcoded 200? */) )
		{
			return false;
		}
		if (0 == BindIoCompletionCallback((HANDLE)Listener, Maxsi::ME_ServerClient__DoneIO, 0) )
		{
			return false;
		}
	}

	Clients					=	new ME_ServerClient*[MaxConnections];

	for (i = 0; i < MaxConnections; i++)
	{
		Clients[i]			=	CreateIOCPClient();
		Clients[i]->PeerId	=	i;
		Clients[i]->Server	=	this;
		Clients[i]->InitializeBuffers();
		OnClientCreated(Clients[i]);
	}

	while (ShouldMainLoop)
	{
		DWORD Result = MsgWaitForMultipleObjectsEx (1,&TerminateEvent,(DWORD)MaintenanceTime,QS_ALLPOSTMESSAGE,MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);

		if ( Result == WAIT_TIMEOUT )
		{
			for (i = 0; i < MaxConnections; i++)
			{
				Clients[i]->Maintenance();
			}
		}
		else if ( Result == WAIT_OBJECT_0 + 1 )
		{
			MSG		msg			=	{0};
			GetMessage( &msg, NULL, 0, 0 );
			TranslateMessage(&msg);
			DispatchMessage(&msg); 
		}
		else
		{
			//int leet = 1337;
			break;
		}
	}

	return true;
}

LINK	void CALLBACK ME_ServerClient__DoneIOWSA(DWORD dwErrorCode,
					 DWORD dwNumberOfBytesTransferred,
					 LPOVERLAPPED lpOverlapped,
					 DWORD dwFlags)
{
	return	ME_ServerClient__DoneIO(dwErrorCode,dwNumberOfBytesTransferred,lpOverlapped);
}

LINK	void CALLBACK ME_ServerClient__DoneIO(DWORD dwErrorCode,
					 DWORD dwNumberOfBytesTransferred,
					 LPOVERLAPPED lpOverlapped)
{
	OVERLAPPED_PLUS*	Ov		=	(OVERLAPPED_PLUS*)lpOverlapped;
	if (0 == dwErrorCode && Ov->Client)
	{
		Ov->Client->DoneIO(dwErrorCode,dwNumberOfBytesTransferred,lpOverlapped);
	}
	return;
}

bool	ME_IOCPServer::ShutDown()
{
	if ( TerminateEvent ) { SetEvent(TerminateEvent); } 
	ShouldMainLoop	=	false;
	return BaseClass::ShutDown();
}

bool	ME_IOCPServer::OnClientCreated	(ME_ServerClient* Client)
{
	Client->Init();
	return true;
}

bool	ME_IOCPServer::OnClientConnected	(SOCKET s, int Error)
{
	for (size_t i = 0; i < MaxConnections; i++)
	{
		if ( Clients[i]->Socket == s )
		{
			if ( Error )
			{
				Clients[i]->Disconnected();
			}
			else
			{
				Clients[i]->Connected();
			}
			return true;
		}
	}
	return false;
}

bool	ME_IOCPServer::OnClientDisconnected	(SOCKET s, int Error)
{
	for (size_t i = 0; i < MaxConnections; i++)
	{
		if ( Clients[i]->Socket == s )
		{
			if ( Error )
			{
				Clients[i]->Close();
				Clients[i]->Init();
			}
			else
			{
				Clients[i]->Disconnected();
			}
			return true;
		}
	}
	return false;
}


//
//	//=============================================================================
//	//	class ME_HTTPServer - Constructors
//	//=============================================================================
//
//	ME_HTTPServer::ME_HTTPServer()
//	{
//	}
//	ME_HTTPServer::~ME_HTTPServer()
//	{		
//	}
//
//	//=============================================================================
//	//	class ME_HTTPServer - Functions
//	//=============================================================================
//
//	bool	ME_HTTPServer::HandleRequest(ME_ServerClient* ServerClient, char* Parameter)
//	{
//		ME_HTTPServerClient*	Client		=	dynamic_cast<ME_HTTPServerClient*>(ServerClient);
//
//		if ( !Client ) { return false; }
//		
//		char* FilePath = "C:\\Documents and Settings\\Administrator\\Skrivebord\\Chuck.S02E21.Chuck.Versus.The.Colonel.HDTV.XviD-2HD.avi";
//		//char* FilePath = "C:\\Documents and Settings\\Administrator\\Skrivebord\\Evasion_01_3.jpg";
//	
//#define		ME_Packet_Default_Size	(512*1024)
//
//		if ( Client->FileHandle == -1 )
//		{
//			Client->FileHandle	=	0;
//			Client->FileSize	=	256*1024*1024;
//			Client->Left		=	Client->FileSize;
//			Client->SetContentLength(Client->FileSize);
//			Client->Data		=	new	char[ME_Packet_Default_Size];
//
//			//// Then open the file for reading
//			//_sopen_s(&Client->FileHandle,FilePath,  O_BINARY | _O_RDONLY, _SH_DENYNO, _S_IREAD);
//			//if ( Client->FileHandle == -1 )
//			//{
//			//	Client->Disconnect();
//			//	return false;
//			//}
//			//else
//			//{
//			//	Client->FileSize	=	_filelength(Client->FileHandle);
//			//	Client->Left		=	Client->FileSize;
//			//	Client->SetContentLength(Client->FileSize);
//			//}
//		}
//
//		
//
//		if ( !Client->IsHeadersSent )
//		{
//			Client->SetResponseCode(200);		
//			Client->SetContentType("application/force-download");
//			Client->SetContentDisposition("attachment; filename=\"Chuck.S02E21.Chuck.Versus.The.Colonel.HDTV.XviD-2HD.avi\";");
//			//Client->SetContentDisposition("attachment; filename=\"Evasion_01_3.jpg\";");
//					
//			//char*	Response	=	"HTTP/1.1 200 OK\r\nDate: Wed, 29 Apr 2009 19:48:00 GMT\r\nServer: Apache\r\nX-Powered-By: PHP/5.2.9\r\nSet-Cookie: sessionid=2c19c0ff5ccf103340f8efca4b0ba3ae; path=/; domain=.maxsi.dk\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nContent-Type: text/plain\r\n\r\n44 \r\nHai Koljan!\r\n\r\nWelcome to the server on Sortie's computer! Muhahaha!\r\n0\r\n\r\n";
//			//char*	Response	=	"Hai Koljan!\r\n\r\nWelcome to the server on Sortie's computer! Muhahaha!";
//			//char*	Response	=	"OH LOLOLOL!\r\n\r\nTHIS IS A TEST, JAWHARHARHARHARHARHARHR-ARGH!!";
//
//
//			Client->WriteHTTPHeaders();
//		}
//
//
//		//char	Response[ME_Packet_Default_Size];
//
//		if (Client->Left>ME_Packet_Default_Size)
//		{
//			//_read(Client->FileHandle,Response,ME_Packet_Default_Size);
//			//Client->WriteHTTP(Response,ME_Packet_Default_Size);
//			Client->WriteHTTP(Client->Data,ME_Packet_Default_Size);
//			Client->Left-=ME_Packet_Default_Size;
//		}
//		else
//		{
//			//_read(Client->FileHandle,Response,Client->Left);
//			//Client->WriteHTTP(Response,Client->Left);
//			Client->WriteHTTP(Client->Data,Client->Left);
//			Client->Left=0;
//			Client->Write(NULL,0);
//			Client->ShouldDisconnect	=	true;
//			//_close(Client->FileHandle);
//			Client->FileHandle	=	-1;
//			delete[] Client->Data;
//		}		
//
//		return true;
//	}
//
//	ME_ServerClient*	ME_HTTPServer::CreateIOCPClient( void )
//	{
//		return (ME_ServerClient*)new ME_HTTPServerClient;
//	}


//=============================================================================
//	class ME_ServerClient
//=============================================================================

ME_ServerClient::ME_ServerClient()
{
	memset(&In,0,sizeof(In));
	memset(&Out,0,sizeof(Out));

	Out.Client			=	this;
	In.Client			=	this;
	
	Out.BufferType		=	ME_BUFFER_TYPE_WRITE;
	In.BufferType		=	ME_BUFFER_TYPE_READ;

	m_State				=	STATE_DEAD;
	m_Mode				=	0;
	m_Command			=	0;
	Socket				=	INVALID_SOCKET;

	Input				=	NULL;
	Output				=	NULL;

	DownSpeed			=	0;
	UpSpeed				=	0;

	RemoteHost			=	0;
	IsThinking			=	false;

	if (!InitializeCriticalSectionAndSpinCount(&MayExecThinkFunction, 
		0x80000400) ) 
	{
		ME_UNDEFINED_BEHAVIOR("ME_ServerClient::ME_ServerClient() couldn't initialize InitializeCriticalSectionAndSpinCount(&MayExecThinkFunction)!");
	}
}

ME_ServerClient::~ME_ServerClient()
{
	DeleteCriticalSection(&MayExecThinkFunction);
}

//=============================================================================
//	class ME_ServerClient - Functions
//=============================================================================

bool ME_ServerClient::InitializeBuffers()
{
	// This should be done in your child classes!
	if ( Input ) { DeleteBuffer(Input); }	Input	=	CreateBuffer(1337);
	if ( Output ) { DeleteBuffer(Output); }	Output	=	CreateBuffer(1337);

	return true;
}

bool ME_ServerClient::TerminateBuffers()
{
	// This should be done in your child classes!
	if ( Input ) { DeleteBuffer(Input); }	Input	=	0;
	if ( Output ) { DeleteBuffer(Output); }	Output	=	0;

	return true;
}

int	ME_ServerClient::Mode()
{
	return m_Mode;
}
int	ME_ServerClient::Command()
{
	return m_Command;
}
int	ME_ServerClient::State()
{
	return m_State;
}
void	ME_ServerClient::SetMode(int NewMode)
{
	m_Mode		=	NewMode;
}
void	ME_ServerClient::SetCommand(int NewCommand)
{
	m_Command	=	NewCommand;
}
void	ME_ServerClient::SetState(int NewState)
{
	if ( NewState == STATE_ACCEPTING )
	{
		ME_DONT_COMPILE_RELEASE_BUILD();
		__debugbreak();
	}
	m_State	=	NewState;
}

void ME_ServerClient::TimeStamp()
{
	time(&dTimeStamp);
}
time_t ME_ServerClient::SecondsElapsed()
{
	time_t timenow;
	time(&timenow);
	return timenow - dTimeStamp;
}
	
void ME_ServerClient::OnConnectionAccepted()	// Move from ACCEPTING to CONNECTED state
{
	int locallen, remotelen;
	sockaddr_in *plocal = 0, *premote = 0;


	if (State() == STATE_ACCEPTING)
	{
		// TODO: I don't think this works!

		Input->Lock(ME_BUFFER_LOCK_PARAMS);

		Input->Clear();

		IsReading	=	TRUE;

		GetAcceptExSockaddrs(Input->GetPointer(),
			Input->GetPointerSizeFollowingData(),
			sizeof(sockaddr_in) + 16,
			sizeof(sockaddr_in) + 16,
			(sockaddr **)&plocal,
			&locallen,
			(sockaddr **)&premote,
			&remotelen);

		if ( !plocal || !premote) { return; }

		memcpy(&LocalAddr, plocal, sizeof(sockaddr_in));
		memcpy(&RemoteAddr, premote, sizeof(sockaddr_in));

		char*	Event	=	BuildString(5,
			"Local (",inet_ntoa(plocal->sin_addr),") "
			"accepted connection from "
			"Remote (",inet_ntoa(premote->sin_addr),")");

		Server->Log(Event);

		delete[] Event;

		if (BindIoCompletionCallback((HANDLE)Socket, ME_ServerClient__DoneIO, 0))
		{
			SetState(STATE_CONNECTED);
			TimeStamp();
		}
		else
		{
			ME_UNDEFINED_BEHAVIOR("ME_ServerClient::OnConnectionAccepted() couldn't BindIoCompletionCallback!");
		}

		Input->Unlock();
	}
}

bool ME_ServerClient::Init()
{
	IsWriting			=	false;
	IsReading			=	false;
	ShouldDisconnect	=	false;

	if (!Input) { return false; }
	if (!Output) { return false; }

	if ( Socket == INVALID_SOCKET )
	{
		Socket = socket(AF_INET, SOCK_STREAM, 0);
		if (Socket == INVALID_SOCKET)
		{
			ME_UNDEFINED_BEHAVIOR("ME_ServerClient::Init() couldn't create a socket!");
			// Problem.
			SetState(STATE_DEAD);
			return false;
		}

		unsigned long	nRead = 0; // Todo: do we really need this? Please remove if not.
		// Note: The above value is not set if the AcceptEx function completes Async (as planned)

		Input->Lock(ME_BUFFER_LOCK_PARAMS);

		// Todo:	If this function writes to input, the pointer IS NOT progressed and the data will be lost!
		//			Please fix that.

		if (!AcceptEx(Server->Listener,	
			Socket,
			Input->GetPointer(),
			Input->GetPointerSizeFollowingData()-sizeof(sockaddr_in) - 16-sizeof(sockaddr_in) - 16,
			sizeof(sockaddr_in) + 16,
			sizeof(sockaddr_in) + 16,
			&nRead,
			&In))
		{
			if (WSAGetLastError() != ERROR_IO_PENDING)
			{
				// Problem.
				SetState(STATE_DEAD);
				closesocket(Socket);
				Input->Unlock();
				return false;
			}
		}
		else
		{
			// How do we handle this?
		}

		Input->Unlock();
	}

	SetState(STATE_ACCEPTING);

	return true;
}

bool ME_ServerClient::Close()
{
	Server->Log("Closed");
	if (State() != STATE_DEAD)
	{
		if (State() == STATE_CONNECTED)
		{
			//struct linger li = {0, 0};	// Default: SO_DONTLINGER

			shutdown(Socket, SD_BOTH);
			//setsockopt(Socket, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li));
		}
		closesocket(Socket);

		Disconnected();

		SetState(STATE_DEAD);
	}
	ShouldDisconnect	=	false;
	Socket				=	INVALID_SOCKET;

	return true;
}


bool ME_ServerClient::Connect(char*	Host)
{
	return false;
}

bool ME_ServerClient::Connected()
{
	return false;
}

bool ME_ServerClient::Disconnected()
{
	return false;
}

void CALLBACK ME_ServerClient::DoneIO(DWORD dwErrorCode,
					 DWORD dwNumberOfBytesTransferred,
					 LPOVERLAPPED lpOverlapped)
{
	OVERLAPPED_PLUS*	ov		=	(OVERLAPPED_PLUS *)lpOverlapped;

	if (0 != dwNumberOfBytesTransferred)
	{
		// Let's rock
		// First, see which type of operation completed: read or write.
		if (ov->BufferType == ME_BUFFER_TYPE_WRITE)
		{
			//char	WroteStr[33];
			//_itoa_s(dwNumberOfBytesTransferred,WroteStr,33,10);
			//char*	Event	=	BuildString(3,"Wrote ",WroteStr," bytes");
			//Server->LogDebug(Event);
			//delete[] Event;

			// Finished a send
			Output->Lock(ME_BUFFER_LOCK_PARAMS);
			Output->ClearInUse();
			Output->Unlock();
		}
		else
		{

			Input->Lock(ME_BUFFER_LOCK_PARAMS);
			Input->ClearInUse();
			Input->SetBufferUsed(Input->GetBufferUsed() + dwNumberOfBytesTransferred);
			Input->Unlock();

			EnterCriticalSection(&MayExecThinkFunction);

			// Finished a read
			OnConnectionAccepted();

			LeaveCriticalSection(&MayExecThinkFunction);

		}
	}
	else
	{
		int Error	=	GetLastError();

		if ( Error && Error != ERROR_IO_PENDING )
		{
			if ( Error == ERROR_ALREADY_EXISTS )
			{
				ME_UNDEFINED_BEHAVIOR("GetLastError() is ERROR_ALREADY_EXISTS. This is most likely caused by calling the Function CreateFile with CREATE_ALWAYS when the file already exists, and the developer forgot to reset the error.");
			}
			else
			{
				Disconnect();
			}
		}
	}

	MaintainConnection();
}

//bool	ME_ServerClient::OnRecieved(size_t Length)
//{
//	TimeStamp();
//	buf_in_used		+=	Length;

//	if ( buf_in_used > IN_BUFFER_SIZE() )
//	{
//		ME_UNDEFINED_BEHAVIOR("buf_in_used > IN_BUFFER_SIZE() in ME_ServerClient! Received more data than in the buffer!");
//	}

//	// Simply reset these, mark the offset part as part of the discardable buffer,
//	// so it can be discarded when no longer required!
//	buf_in_used		+=	buf_in_offset;
//	buf_in_offset	=	0;

//	return true;
//}
//bool	ME_ServerClient::Read(size_t Keep)
//{
//	bool	Result	=	true;

//	EnterInUse();

//	if ( Keep )
//	{
//		memcpy(buf_in_actual,buf_in_actual+buf_in_offset+buf_in_used-Keep,Keep);
//	}
//	buf_in_offset	=	Keep;
//	buf_in_used		=	0;


//	if (State() == STATE_CONNECTED)
//	{			
//		if (0 == ReadFile((HANDLE)Socket,
//			buf_in_actual+buf_in_offset,
//			IN_BUFFER_SIZE()-buf_in_offset,
//			&nRead,
//			&In))
//		{
//			int	nLastError = GetLastError();
//			if (ERROR_IO_PENDING != nLastError)
//			{
//				Result	=	false;
//			}
//		}
//	}
//	else
//	{
//		Result	=	false;
//	}

//	LeaveInUse();

//	return Result;
//}

bool ME_ServerClient::Disconnect()
{
	Server->Log("Disconnected");
	Close();
	Init();

	return true;
}

bool ME_ServerClient::MaintainConnection()
{
	TimeStamp();

	// Do not disconnect if we are still writing, this function will be recalled when the write is finished
	if (ShouldDisconnect && !IsWriting && !IsReading )
	{
		Disconnect();
	}
	else
	{
		DoThink();
	}

	return true;
}

bool ME_ServerClient::Think()
{
	// Remember to set this properly in child classes.
	ScheduleNextThink(ME_NEXT_THINK_DONT);

	// This should be overriden in child classes!
	return true;
}

bool ME_ServerClient::DoThink()
{
	EnterCriticalSection(&MayExecThinkFunction);

	if ( IsThinking )
	{
		ME_UNDEFINED_BEHAVIOR("ME_ServerClient::DoThink() attempted to think inside a think!");
	}

	IsThinking	=	true;

	NextThink	=	ME_NEXT_THINK_NONE;

	Think();

	if ( NextThink == ME_NEXT_THINK_NONE )
	{
#ifdef _DEBUG

		// Debug variables, eases finding where the bug happened

		int	CurrentMode		=	Mode();
		int	CurrentCommand	=	Command();	

#endif
		__debugbreak();
		ME_UNDEFINED_BEHAVIOR("ME_ServerClient::DoThink() had NextThink == ME_NEXT_THINK_NONE! There is a bug in the Think() function!");
	}

	IsThinking	=	false;

	LeaveCriticalSection(&MayExecThinkFunction);

	return true;
}

bool ME_ServerClient::ScheduledNextThink(int NextThinkType, int Line, char* Function, char* FileName)
{
	if ( Server->LogFile != INVALID_HANDLE_VALUE )
	{
		char*	ThinkTypes[]	=	
		{
			"ME_NEXT_THINK_NONE",
			"ME_NEXT_THINK_WINCALLBACK",
			"ME_NEXT_THINK_MECALLBACK",
			"ME_NEXT_THINK_WAIT",
			"ME_NEXT_THINK_DONT",
		};

		char*	LineStr	=	UIToA((unsigned int)Line);

		char*	Event	=	BuildString(7,ThinkTypes[NextThinkType]," ",LineStr,":",Function,"::",FileName);

		Server->Log(Event);

		delete[] Event;
		delete[] LineStr;
	}

	NextThink		=	NextThinkType;

	return true;
}

bool ME_ServerClient::Write(char* Buffer, size_t BufferLen)
{
	if (!Output) { return false; }

	bool	Result	=	true;

	Output->Lock(ME_BUFFER_LOCK_PARAMS);

	if ( Output->IsInUse() == ME_ERROR_SUCCESS && Output->MayWrite() == ME_ERROR_SUCCESS )
	{
		if ( BufferLen > Output->GetBufferSize() )
		{
			ME_UNDEFINED_BEHAVIOR("ME_ServerClient::Write() attempted to write more data to the output buffer than it could handle!");

			Result	=	false;
		}
		else
		{
			Output->Clear();
			Output->Write(Buffer,BufferLen);
			Output->SetInUse();

			ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);

			if ( WriteFile((HANDLE)Socket,
				Output->GetBuffer(),
				BufferLen,
				NULL,
				&Out) == FALSE )
			{
				DWORD nLastError = GetLastError();
				if (ERROR_IO_PENDING != nLastError)
				{
					ME_DONT_COMPILE_RELEASE_BUILD();
					// Is the following correct?


					char*	WroteStr		=	UIToA(nLastError);	
					char*	ErrorMSG		=	BuildString(3,"Write Failure #",WroteStr," in ME_ServerClient::Write() !");
					Server->Log(ErrorMSG);
					ME_UNDEFINED_BEHAVIOR(ErrorMSG);
					Output->ClearInUse();
					ScheduleNextThink(ME_NEXT_THINK_NONE);
					Result	=	false;
					delete[] ErrorMSG;
					delete[] WroteStr;
				}
			}
		}
	}
	else
	{
		Result	=	false;
	}

	Output->Unlock();

	return Result;
}

bool ME_ServerClient::Read(size_t Keep)
{
	if (!Input) { return false; }

	bool	Result	=	true;

	Input->Lock(ME_BUFFER_LOCK_PARAMS);

	if ( Input->IsInUse() == false && Input->MayWrite() == ME_ERROR_SUCCESS)
	{
		if ( Keep > Input->GetBufferUsed() )
		{
			ME_UNDEFINED_BEHAVIOR("ME_ServerClient::Read() attempted to keep more data than was in the buffer.");

			Result	=	false;
		}
		else if ( Keep == 0 )
		{
			// Discard all data into the input buffer and read some more

			Input->Clear();
			Input->SetInUse();

			ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);

			if ( ReadFile((HANDLE)Socket,
				Input->GetBuffer(),
				Input->GetBufferSize(),
				NULL,
				&In) == FALSE )
			{
				DWORD nLastError = GetLastError();
				if (ERROR_IO_PENDING != nLastError)
				{
					ME_DONT_COMPILE_RELEASE_BUILD();
					// Is the following correct?


					char*	WroteStr		=	UIToA(nLastError);	
					char*	ErrorMSG		=	BuildString(3,"Read Failure #",WroteStr," in ME_ServerClient::Read()!");
					Server->Log(ErrorMSG);
					ME_UNDEFINED_BEHAVIOR(ErrorMSG);
					Output->ClearInUse();
					ScheduleNextThink(ME_NEXT_THINK_NONE);
					Result	=	false;
					delete[] ErrorMSG;
					delete[] WroteStr;
				}
			}
		}
		else
		{
			// Keep > 0, so keep the last Keep bytes in the buffer while requesting some more
			// It'd be easier if we simply moved the kept bytes to the start of the buffer,
			// but it saves precious CPU cycles to just leave them where they are and append
			// some data after their location - chance is the (perhaps) few bytes, there is currently
			// room for, is enough to continue the process this client is busy doing.

			// If it's not, then the client will get called to parse the new data, but fail again
			// and then call us and perhaps there is room at the start of the buffer that we can
			// write to. If that isn't enough room to complete the process, then the buffer should
			// have been larger in the first place - or the calling client should have been programmed
			// differently.

			if ( Input->GetBufferSize() <= Keep )
			{
				ME_UNDEFINED_BEHAVIOR("ME_ServerClient::Read() attempted to keep the entire buffer (or more) - This means there is no space for new data!");
			}


			Input->SetInUse();
			Input->ProgressBuffer(Input->GetBufferUsed()-Keep);

			size_t	dP	=	(Input->GetBufferOffset() + Input->GetBufferUsed() ) % Input->GetBufferSize();
			size_t	dL	=	( dP < Input->GetBufferOffset() ) ? Input->GetBufferOffset()-dP : Input->GetBufferSize()-dP;

			ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);

			if ( ReadFile((HANDLE)Socket,
				Input->GetBuffer()+dP,
				dL,
				NULL,
				&In) == FALSE )
			{
				DWORD nLastError = GetLastError();
				if (ERROR_IO_PENDING != nLastError)
				{
					ME_DONT_COMPILE_RELEASE_BUILD();
					// Is the following correct?

					char*	WroteStr		=	UIToA(nLastError);	
					char*	ErrorMSG		=	BuildString(3,"Read Failure #",WroteStr," in ME_ServerClient::Read()!");
					Server->Log(ErrorMSG);
					ME_UNDEFINED_BEHAVIOR(ErrorMSG);
					Output->ClearInUse();
					ScheduleNextThink(ME_NEXT_THINK_NONE);
					Result	=	false;
					delete[] ErrorMSG;
					delete[] WroteStr;
				}
			}
		}
	}
	else
	{
		Result	=	false;
	}

	Input->Unlock();

	return Result;
}

//bool ME_ServerClient::Write(char* Buffer, size_t BufferLen)
//{
//	char	WroteStr[33];
//	_itoa_s(BufferLen,WroteStr,33,10);
//	char*	Event	=	BuildString(3,"Requested a write of ",WroteStr," bytes");
//	Server->LogDebug(Event);
//	delete[] Event;

//	// Add to output buffer.
//	// If no write is currently being performed, we can simply copy the data
//	// to actual_out_buf. However - if actual_out_buf is currently being
//	// touched, we need to store the buffer in out_buffer first.

//	EnterInUse();

//	if (!IsWriting)
//	{
//		//InterlockedIncrement(&IsWriting);

//		// First, make sure that we can send the whole buffer at once. If we
//		// can't, we will do it through the out_buffer class.
//		if (OUT_BUF_SIZE() >= BufferLen)
//		{


//			char	WroteStr[33];
//			_itoa_s(BufferLen,WroteStr,33,10);
//			char*	Event	=	BuildString(3,"ME_ServerClient::Write() Began writing ",WroteStr," bytes");
//			Server->LogDebug(Event);
//			delete[] Event;

//			IsWriting	=	TRUE;	// Can't do further writes, it's underway!

//			// It's ok, let's do it!
//			memcpy(buf_out_actual, Buffer, BufferLen);

//			if ( WriteFile((HANDLE)Socket,
//				buf_out_actual,
//				BufferLen,
//				&nWritten,
//				&Out) == FALSE )
//			{
//				DWORD nLastError = GetLastError();
//				if (ERROR_IO_PENDING != nLastError)
//				{
//					_itoa_s(nLastError,WroteStr,33,10);
//					char*	Error	=	BuildString(3,"Write Failure #",WroteStr," in ME_ServerClient::Write() !");
//					Server->Log(Error);
//					delete[] Error;
//				}
//				else
//				{
//					Server->LogDebug("Pending IO in ME_ServerClient::Write()");
//				}
//			}
//		}
//		else
//		{
//			// Ok, the buffer is too large to be contained in
//			// actual_out_buffer, so let's do this in phases, using
//			// out_buffer.
//			buf_out->AddToBuffer(Buffer, BufferLen);

//			PendingWrite = TRUE;

//			NetWrite();
//		}
//	}
//	else
//	{
//		// A send is currently underway.
//		// Do it through actual_out_buffer.
//		buf_out->AddToBuffer(Buffer, BufferLen);

//		PendingWrite = TRUE;
//	}

//	LeaveInUse();

//	/*
//		The EnterInUse() and LeaveInUse() functions are needed so there
//		won't be a race condition, when two threads are calling this function
//		for the same client simultaneously. If, however, because of the
//		nature of your application, such thing cannot happen, you can
//		override those functions to do nothing, in order to improve
//		performance.
//	*/

//	return true;
//}

//bool ME_ServerClient::NetWrite()
//{
//	int nLength;

//	nLength = OUT_BUF_SIZE();

//	buf_out->GetBuffer(buf_out_actual, &nLength);
//	if (buf_out->IsEmpty())
//	{
//		PendingWrite	=	FALSE;
//	}
//	else
//	{
//		PendingWrite	=	TRUE;
//	}

//	char	WroteStr[33];
//	_itoa_s(nLength,WroteStr,33,10);
//	char*	Event	=	BuildString(3,"ME_ServerClient::NetWrite() Began writing ",WroteStr," bytes");
//	Server->LogDebug(Event);
//	delete[] Event;

//	nWritten	=	0;
//	IsWriting	=	TRUE;	// Can't do further writes, it's underway!

//	if ( WriteFile((HANDLE)Socket,
//		buf_out_actual,
//		nLength,
//		&nWritten,
//		&Out) == FALSE )
//	{
//		DWORD nLastError = GetLastError();
//		if (ERROR_IO_PENDING != nLastError)
//		{
//			_itoa_s(nLastError,WroteStr,33,10);
//			char*	Error	=	BuildString(3,"Write Failure #",WroteStr," in ME_ServerClient::NetWrite() !");
//			Server->Log(Error);
//			delete[] Error;
//		}
//		else
//		{
//			Server->LogDebug("Pending IO in ME_ServerClient::NetWrite()");
//		}
//	}
//	return true;
//}

void ME_ServerClient::Maintenance()
{
	/*
		Check if client is an abuser.
		Abusing clients are:
		1. Clients that connect without sending data, thus not allowing 
		   AcceptEx to return.
		2. Clients that connect, send something, and remain connected
		   for too long.

		(there are other kinds of abusive clients, but only these two kinds
		need to be looked for at a periodic manner.)
	*/

	int nSeconds = 0;
	int nBytes;

	nBytes = sizeof(nSeconds);

	// See if client has attempted to connect without sending data.
	if (State() == STATE_ACCEPTING)
	{
		if (0 == getsockopt(Socket, 
			SOL_SOCKET, 
			SO_CONNECT_TIME, 
			(char *)&nSeconds, 
			&nBytes))
		{
			// Client has been connected for nSeconds so far
			if (nSeconds != -1)
			{
				if (nSeconds > MAX_CONNECT_SECONDS)
				{
					Disconnect();
				}
			}
		}
	}
	else if (State() == STATE_CONNECTED)
	{
		//Server->Log("Evaluating if I should disconnect");
		//if (SecondsElapsed() > MAX_SILENT_SECONDS)
		//{
		//	Disconnect();
		//}
	}
}

////=============================================================================
////	class ME_HTTPServerClient - Constructors
////=============================================================================

//ME_HTTPServerClient::ME_HTTPServerClient()
//{
//	FileHandle			=		-1;
//}
//ME_HTTPServerClient::~ME_HTTPServerClient()
//{
//}

////=============================================================================
////	class ME_HTTPServerClient - Functions
////=============================================================================
//
//bool	ME_HTTPServerClient::WriteHTTPHeaders( void )
//{
//	if ( IsHeadersSent ) { return false; }

//	//char*	Response	=	"HTTP/1.1 200 OK\r\nDate: Wed, 29 Apr 2009 19:48:00 GMT\r\nServer: Apache\r\nX-Powered-By: PHP/5.2.9\r\nSet-Cookie: sessionid=2c19c0ff5ccf103340f8efca4b0ba3ae; path=/; domain=.maxsi.dk\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nContent-Type: text/plain\r\n\r\n44 \r\nHai Koljan!\r\n\r\nWelcome to the server on Sortie's computer! Muhahaha!\r\n0\r\n\r\n";
//	
//	char	ResponseCodeSTR[4];
//	char	ContentLengthSTR[11];

//	_itoa_s(ResponseCode,ResponseCodeSTR,4,10);
//	_itoa_s(ContentLength,ContentLengthSTR,11,10);

//	char*	Header	=	BuildString(9,
//		"HTTP/1.1 ",
//		ResponseCodeSTR,
//		" OK\r\n"
//		//"Date: Wed, 29 Apr 2009 19:48:00 GMT\r\n"
//		"Server: MaxsiEngineHTTPServer\r\n"
//		"Connection: close\r\n"
//		"Transfer-Encoding: chunked\r\n"
//		"Content-Type: ",
//		ContentType,"\r\n"
//		"Content-Disposition: ",
//		ContentDisposition,"\r\n"
//		"Content-Length: ",
//		ContentLengthSTR,"\r\n"
//		"\r\n"
//		);

//	Write(Header,strlen(Header));

//	IsHeadersSent	=	true;

//	return true;
//}
//bool	ME_HTTPServerClient::WriteHTTP(char* Buffer, size_t BufferLen)
//{
//	if ( !IsHeadersSent ) {  WriteHTTPHeaders(); }

//	char*	NL				=	"\r\n";
//	size_t	NLL				=	strlen(NL);
//	char	ChunckSize[17];

//	_itoa_s((int)BufferLen,ChunckSize,17,16);

//	size_t	ChunckSizeLen	=	strlen(ChunckSize);
//	size_t	ResponseLen		=	ChunckSizeLen+NLL+BufferLen+NLL+1;
//	char*	Response		=	new	char[ResponseLen];
//	Response[ResponseLen-1]	=	0;
//	memcpy(Response,ChunckSize,ChunckSizeLen);
//	memcpy(Response+ChunckSizeLen,NL,NLL);
//	memcpy(Response+ChunckSizeLen+NLL,Buffer,BufferLen);
//	memcpy(Response+ChunckSizeLen+NLL+BufferLen,NL,NLL);

//	bool	Result	=	Write(Response,ResponseLen-1);

//	delete[]	Response;

//	return Result;
//}
//bool	ME_HTTPServerClient::Init()				// Attach to a new client
//{
//	Headers			=	CreateList(0);
//	IsHeadersSent	=	false;
//	ResponseCode	=	200;
//	ContentType		=	0;

//	return BaseClass::Init();
//}
//bool	ME_HTTPServerClient::Close()				// Detach from a client
//{
//	DeleteList(Headers);		Headers	=	0;
//	IsHeadersSent	=	false;
//	if ( ContentType ) { delete[]	ContentType; ContentType = 0; }
//	return BaseClass::Close();
//}	
//bool	ME_HTTPServerClient::OnRecieved(size_t Length)
//{
//	if ( Server->HandleRequest(this,0) )
//	{
//		// Disconnect
//		return true;
//	}
//	// Keep connection alive
//	return true;
//}
//bool	ME_HTTPServerClient::SetResponseCode(int NewResponseCode)
//{
//	ResponseCode		=	NewResponseCode;
//	return true;
//}
//int		ME_HTTPServerClient::GetResponseCode()
//{
//	return	ResponseCode;
//}
//bool	ME_HTTPServerClient::SetContentType(char* NewContentType)
//{
//	ContentType		=	BuildString(1,NewContentType);
//	return true;
//}
//char*	ME_HTTPServerClient::GetContentType()
//{
//	return	ContentType;
//}
//bool	ME_HTTPServerClient::SetContentDisposition(char* NewContentDisposition)
//{
//	ContentDisposition		=	NewContentDisposition;
//	return true;
//}
//char*	ME_HTTPServerClient::GetContentDisposition()
//{
//	return	ContentDisposition;
//}
//bool	ME_HTTPServerClient::SetContentLength(int NewContentLength)
//{
//	ContentLength		=	NewContentLength;
//	return true;
//}
//int		ME_HTTPServerClient::GetContentLength()
//{
//	return	ContentLength;
//}

EndMaxsiNamespace

#endif
